package cn.edu.scau.dormitory_management_system.service.impl;

import cn.edu.scau.dormitory_management_system.entity.DormbuildEntity;
import cn.edu.scau.dormitory_management_system.entity.StudentEntity;
import cn.edu.scau.dormitory_management_system.entity.SysLogEntity;
import cn.edu.scau.dormitory_management_system.entity.dto.LogDTO;
import cn.edu.scau.dormitory_management_system.entity.dto.ReportDTO;
import cn.edu.scau.dormitory_management_system.exception.pojo.MyException;
import cn.edu.scau.dormitory_management_system.enums.ExceptionMsgEnum;
import cn.edu.scau.dormitory_management_system.module.assign_dormitory.service.AssignDormitory;
import cn.edu.scau.dormitory_management_system.service.DormbuildService;
import cn.edu.scau.dormitory_management_system.service.ImportService;
import cn.edu.scau.dormitory_management_system.service.StudentService;
import cn.edu.scau.dormitory_management_system.service.SysLogService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author djc
 * @date 2021/8/9
 * @time 9:35
 */
@Service
public class ImportServiceImpl implements ImportService {

    @Autowired
    private SysLogService logService;
    @Autowired
    private StudentService studentService;
    @Autowired
    private DormbuildService dormbuildService;

    @Autowired
    private LogDTO logDTO;

    private String optionTable;

    private void addLog(long ms, int sum, String op) {
        SysLogEntity logEntity = new SysLogEntity();
        logEntity.setIpAddr(logDTO.getIp());
        logEntity.setCity(logDTO.getCity());
        logEntity.setUserName(logDTO.getUserName());
        logEntity.setExeTime(ms);
        String ops = "";
        if(sum > 1) ops = "批量";
        logEntity.setOperation("在表" + optionTable + "中" + ops + op + sum + "条记录");
        logEntity.setOpTime(new Date());
        logService.save(logEntity);
    }

    @Autowired
    private AssignDormitory assignDormitory;
    /**
     *  记录共导入多少条数据，其中修改了多少条，添加了多少条
     *  注意序列化
     */
    private <T> ReportDTO importToSQL(IService<T> iService, List<T> list) {
        ReportDTO dto = new ReportDTO(0,0,0);
        List<T> addList = new ArrayList<>();
        List<T> updateList = new ArrayList<>();
        for (T t : list) {
            T tmp = iService.getById((Serializable) t);
            if(tmp == null) {
                addList.add(t);
                dto.incrAddNumber();
            }else {
                updateList.add(t);
                dto.incrUpdateNumber();
            }
        }
        try {
            Date now;
            if(!addList.isEmpty()) {
                now = new Date();
                iService.saveBatch(addList);
                long spend = new Date().getTime() - now.getTime();
                addLog(spend, dto.getAddNumber(), "插入");
                if(optionTable.equals("tb_student")) {
                    //宿舍分配
                    assignDormitory.insertList((List<StudentEntity>) addList);
                }else {
                    dormbuildService.createList((List<DormbuildEntity>) addList);
                }
            }

            if(!updateList.isEmpty()) {
                if("tb_dormbuild".equals(optionTable)) {
                    dto.setFailure(dto.getUpdateNumber());
                    dto.setUpdateNumber(0);
                    return dto;
                }
                now = new Date();
                iService.updateBatchById(updateList);
                long spend = new Date().getTime() - now.getTime();
                addLog(spend, dto.getUpdateNumber(), "修改");
            }
            return dto;
        }catch (Exception ex) {
            throw new MyException(ExceptionMsgEnum.IMPORT_DATA_TO_SQL_ERROR);
        }
    }

    @Override
    public ReportDTO importFile(MultipartFile file, int option) {
        if(file == null) {
            throw new MyException(ExceptionMsgEnum.NO_CHOSE_IMPORT_FILE);
        }
        String filename = file.getOriginalFilename();
        assert filename != null;
        if(filename.endsWith(".xls") || filename.endsWith(".xlsx")) {
            InputStream inputStream;
            try {
                inputStream = file.getInputStream();
            } catch (IOException e) {
                throw new MyException(ExceptionMsgEnum.CATCH_FILE_INPUT_STREAM_ERROR);
            }

            Workbook workbook;
            if(filename.endsWith(".xls")) {
                try {
                    workbook = new HSSFWorkbook(new POIFSFileSystem(inputStream));
                } catch (Exception e) { //需继续判断文件是否是由xlsx改过来的
                    try {
                        workbook = new XSSFWorkbook(inputStream);
                    } catch (Exception ex) {
                        throw new MyException(ExceptionMsgEnum.CATCH_FILE_INPUT_STREAM_ERROR);
                    }
                }
            }else {
                try {
                    workbook = new XSSFWorkbook(inputStream);
                } catch (Exception e) {
                    throw new MyException(ExceptionMsgEnum.CATCH_FILE_INPUT_STREAM_ERROR);
                }
            }
            return getTByOption(workbook.getSheetAt(0), option);
        }
        return null;
    }

    private ReportDTO getTByOption(Sheet sheet, int option) {
        int numberOfCells = sheet.getPhysicalNumberOfRows();

        switch (option) {
            case 0:
                List<StudentEntity> list = new ArrayList<>();
                for (int i = 1; i < numberOfCells; i++) {
                    list.add(new StudentEntity());
                }
                getContent(sheet, list);
                for (StudentEntity entity : list) {
                    QueryWrapper<StudentEntity> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("stu_id", entity.getStuId());
                    StudentEntity one = studentService.getOne(queryWrapper);
                    if(one!=null) {
                        if(one.getSid()!=null) {
                            entity.setSid(one.getSid());
                        }
                    }
                }
                optionTable = "tb_student";
                return importToSQL(studentService, list);
            case 1:
                List<DormbuildEntity> list1 = new ArrayList<>();
                for (int i = 1; i < numberOfCells; i++) {
                    list1.add(new DormbuildEntity());
                }
                getContent(sheet, list1);
                for (DormbuildEntity entity : list1) {
                    QueryWrapper<DormbuildEntity> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("dorm_build_name", entity.getDormBuildName());
                    DormbuildEntity one = dormbuildService.getOne(queryWrapper);
                    if(one!=null) {
                        if(one.getBuildId()!=null) {
                            entity.setBuildId(one.getBuildId());
                        }
                    }
                }
                optionTable = "tb_dormbuild";
                return importToSQL(dormbuildService, list1);
            default:
                break;
        }

        return null;
    }

    private <T> void getContent(Sheet sheet, List<T> list) {
        int rows = sheet.getPhysicalNumberOfRows();
        int cells = sheet.getRow(0).getPhysicalNumberOfCells();

        for (int i = 1; i < rows; i++) {
            Row row = sheet.getRow(i);

            int n = 0;

            T t = list.get(i - 1);
            Class<?> aClass = t.getClass();
            Field[] fields = new Field[cells];

            //去掉不需要的字段
            for (Field field : aClass.getDeclaredFields()) {
                if ("serialVersionUID".equals(field.getName())) continue;
                if("sid".equals(field.getName())) continue;
                if("buildId".equals(field.getName())) continue;
                try {
                    fields[n++] = field;
                }catch (Exception ex) {
                    throw new MyException(ExceptionMsgEnum.FILE_FORMAT_ERROR);
                }
            }

            if(n != cells) {
                throw new MyException(ExceptionMsgEnum.FILE_FORMAT_ERROR);
            }

            for (int j = 0; j < cells; j++) {
                Cell cell = row.getCell(j);

                /**
                 * 全转成String
                 */
                String value = getCellValue(cell);
                if(value == null) {
                    throw new MyException(ExceptionMsgEnum.FILE_FORMAT_ERROR);
                }

                fields[j].setAccessible(true);
                Class<?> type = fields[j].getType();

                /**
                 * 根据typeName赋值
                 */
                if(type.getName().endsWith("Integer")) {
                    try {
                        if("男".equals(value)) {
                            fields[j].set(t, 1);
                        }else if("女".equals(value)) {
                            fields[j].set(t, 0);
                        }else {
                            if(value.endsWith(".0")) value = value.substring(0, value.lastIndexOf('.'));
                            fields[j].set(t, Integer.parseInt(value));
                        }
                    } catch (IllegalAccessException e) {
                        throw new MyException(ExceptionMsgEnum.FILE_FORMAT_ERROR);
                    }
                }else if(type.getName().endsWith("String")) {
                    try {
                        fields[j].set(t, value);
                    } catch (IllegalAccessException e) {
                        throw new MyException(ExceptionMsgEnum.FILE_FORMAT_ERROR);
                    }
                }else if(type.getName().endsWith("Date") || type.getName().endsWith("Timestamp")) {
                    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    try {
                        Date date = df.parse(value);
                        Timestamp timestamp = new Timestamp(date.getTime());
                        fields[j].set(t, timestamp);
                    } catch (Exception e) {
                        throw new MyException(ExceptionMsgEnum.FILE_FORMAT_ERROR);
                    }
                }else if(type.getName().endsWith("Double")){
                    try {
                        fields[j].set(t, Double.parseDouble(value));
                    } catch (Exception e) {
                        throw new MyException(ExceptionMsgEnum.FILE_FORMAT_ERROR);
                    }
                }else {
                    throw new MyException(ExceptionMsgEnum.NO_FOUND_MATCH_TYPE);
                }
            }
        }

    }

    public String getCellValue(Cell cell) {
        String temp = null;
        if (cell == null) {
            return null;
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getRichStringCellValue().getString();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    Date date = cell.getDateCellValue();
                    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    temp = df.format(date);
                } else {
                    DecimalFormat df=new DecimalFormat("0");
                    temp = df.format(cell.getNumericCellValue());
                }
            default:
                return temp;
        }
    }
}
